package org.homs.continuations;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.continuations.Continuation;

// {cont:continuation, model:{meta:{type:""}, data:{}}}
public class Fumanchou {

	private static final String CONT_FIELD = "cont";
	public static final String META_FIELD = "meta";
	public static final String DATA_FIELD = "data";
	public static final String MODEL_FIELD = "model";

	protected final Map<String, Object> struct;

	public Fumanchou() {
		this.struct = new HashMap<String, Object>();
		// o.put("cont", undefined);
		this.struct.put(MODEL_FIELD, new HashMap<String, Object>());
		setToModel(DATA_FIELD, new HashMap<String, Object>());
		setToModel(META_FIELD, new HashMap<String, Object>());
	}

	public Fumanchou(final NativeObject nativeObject) {
		this.struct = js2j(nativeObject);
	}

	@SuppressWarnings("unchecked")
	public Fumanchou setToModel(final String name, final Object value) {
		((Map<String, Object>) this.struct.get(MODEL_FIELD)).put(name, value);
		return this;
	}

	public Fumanchou setToModelData(final String name, final Object value) {
		getModelData().put(name, value);
		return this;
	}

	public Fumanchou setToModelMeta(final String name, final Object value) {
		getModelMeta().put(name, value);
		return this;
	}

	public NativeObject getAsNativeObject() {
		return j2js(this.struct);
	}

	public Map<String, Object> getAsJavaMap() {
		return this.struct;
	}

	@SuppressWarnings("unchecked")
	public Map<String, Object> getModel() {
		return (Map<String, Object>) this.struct.get(MODEL_FIELD);
	}

	public Continuation getContinuation() {
		return (Continuation) this.struct.get(CONT_FIELD);
	}

	@SuppressWarnings("unchecked")
	public Map<String, Object> getModelData() {
		return (Map<String, Object>) getModel().get(DATA_FIELD);
	}

	@SuppressWarnings("unchecked")
	public Map<String, Object> getModelMeta() {
		return (Map<String, Object>) getModel().get(META_FIELD);
	}

	@SuppressWarnings("unchecked")
	public Object getModelData(final String name) {
		return ((Map<String, Object>) getModel().get(DATA_FIELD)).get(name);
	}

	@SuppressWarnings("unchecked")
	public Object getModelMeta(final String name) {
		return ((Map<String, Object>) getModel().get(META_FIELD)).get(name);
	}

	@SuppressWarnings("unchecked")
	public static NativeObject j2js(final Map<String, Object> model) {
		final NativeObject nativeParams = new NativeObject();

		for (final Entry<String, Object> e : model.entrySet()) {
			if (e.getValue() instanceof Map<?, ?>) {
				nativeParams.put(e.getKey(), nativeParams, j2js((Map<String, Object>) e.getValue()));
			} else {
				nativeParams.put(e.getKey(), nativeParams, e.getValue());
			}
		}

		return nativeParams;
	}

	public static Map<String, Object> js2j(final NativeObject nativeObject) {
		final Map<String, Object> result = new HashMap<String, Object>();
		for (final Object key : nativeObject.getAllIds()) {
			Object value = nativeObject.get(key.toString(), nativeObject);
			if (value instanceof Undefined) {
				value = null;
			} else if (value instanceof NativeObject) {
				value = js2j((NativeObject) value);
			}
			result.put(key.toString(), value);
		}
		return result;
	}

	@Override
	public String toString() {
		return new StringBuilder() //
				.append("{model=")//
				.append(getModel().toString())//
				.append(",cont=")//
				.append(getContinuation() == null ? "null" : "Continuation")//
				.append("}")//
				.toString();
	}
}
